#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "download_pieces.h"


#include "structure.h"
#include "toolkit.h"
#include "config.h"
#include "DEV/table_fichiers.h"

//-------------Elements ajoutés-------------------------


//------------------------------------------------------
//

typedef int gp_file; // descripteur de fichier dans la base de donnée db

typedef long long _t_buffermap ;

extern struct database db;

int choosePieces(const struct file * f, const struct client * peer, const int n, _t_buffermap * pieces);

struct down_args {
   struct client * cl;
   _t_buffermap  bm;
   int gp_file ;
};


struct file * get_file_from_id(size_t id){
   return &db.tab_file[id] ;
}


_t_buffermap BM_empty(_t_buffermap m){
   return 0;
}


_t_buffermap BM_set(size_t i, _t_buffermap m){
    return  m | 1 << i;
}

short BM_isSet(size_t i, _t_buffermap m){
   return m & 1 << i ;
}

size_t BM_size(_t_buffermap m){
   return sizeof(m);
}


void client_lock_mutex(struct client *c){
   pthread_mutex_lock(&c->mutex);
}

void client_unlock_mutex(struct client *c){
   pthread_mutex_unlock(&c->mutex);
}

short isDownloading(struct client *c){
   return c->downloading;
}

void client_set_downloading(struct client *c){
   c->downloading = 1;
}

_t_buffermap intBufToBM(int * buf, size_t size,_t_buffermap m){
   size_t i ;
   m = BM_empty(m);

   for(i=0; i<size; i++){
      m = BM_set(buf[i],m);
   }
   return m;
}

int buffermap_full(struct file f){
  int i;
  int res = f.size%f.piece_size;
  int nb_pieces;
  if(res == 0)
    nb_pieces = f.size/f.piece_size;
  else 
    nb_pieces = f.size/f.piece_size +1;
  for(i=0;i<nb_pieces;i++){
    if(BM_isSet(i,f.buffer_map) == 0 )
      return 0;
  }
  return 1;
}


void pthread_join_all(pthread_t *t, int size){
  int i;
  for(i = 0; i < size; i++)
    pthread_join(t[i],NULL);
}


/*
 * Pour le fichier désigné par le descripteur gp_file,
 * choisit un pair et une liste de pièces à télécharger.
 * Renvoi le résultat par effets de bord cl et pieces.
 * Attention, le _t_buffermap est vidé.
 *
 * Le mutex du pair choisi est verrouillé.
 *
 * Retourne 0 en cas de succès.
 *         -1 si aucun pair n'a été trouvé
 */

int choose(int gp_file, struct client ** cl, _t_buffermap * pieces){
   struct file *f = get_file_from_id(gp_file);

   *pieces = BM_empty(*pieces);

   int nbPiecesMax = MAX_PIECES; 
   fprintf(stderr,"<<<<<<\n");

   /*Recherche d'un client qui ne télécharge pas*/
   struct client * peer ;
   for(peer = f->clients ; peer != NULL; peer = peer->next){
      client_unlock_mutex(peer);

      if(choosePieces(f,peer,nbPiecesMax,pieces)){
         client_set_downloading(peer);
         *cl = peer;
         return 0; //ok
         //on ne dévérouille pas le mutex
      }

      //sinon, pas de pièces, on regarde le pair suivant.

      client_unlock_mutex(peer);
   }

   assert(peer == NULL);
   return -1; //Aucun disponible trouvé
}
			
	
	
/* Pour un fichier f, un pair peer et un entier n
 * choisi au plus n pièces à télécharger
 * et les indiques dans le _t_buffermap pieces par effets de bord
 *
 * retourne le nb de pièces choisies
 * (potentiellement 0)
 */
int choosePieces(const struct file * f, const struct client * peer, const int n, _t_buffermap * pieces){
   _t_buffermap myBM = f->buffer_map;
   size_t bmLen = BM_size(myBM); 


   /* Recherches de pieces à télécharger dans la limite du 
    * de la taille des messages finaux
    */

   int nbPieces = 0 ;
   size_t i;

   _t_buffermap hisBM = peer->client_buffer_map;

   for(i=0 ; i< bmLen ; i++)
      if(! BM_isSet(i,myBM) && BM_isSet(i,hisBM) && nbPieces < n){
         *pieces = BM_set(i, *pieces);
         nbPieces++;
      }

   return nbPieces ;
}

void MAJ_buffermap(_t_buffermap *buffermap,_t_buffermap pieces){
  *buffermap = *buffermap | pieces;
}

void get_pieces_func(void * v_gp_file){
#define MAX_THREAD 10
   pthread_t tab[MAX_THREAD];
   struct down_args tab_args[MAX_THREAD];
   int gp_file = *(int*) v_gp_file;

   db.tab_file[gp_file].type = LEECH;

   _t_buffermap pieces;
   struct client * cl;
   int thread_number = 0;

   while(! buffermap_full(db.tab_file[gp_file])){
      fprintf(stderr,"-------\n");

      if(thread_number == MAX_THREAD){
	pthread_join_all(tab, MAX_THREAD); 
        thread_number = 0;
      }

      //choix du client et des pièces à télécharger.
      if( -1 == choose(gp_file, &cl, &pieces)){
         sleep(3);
         continue;
      }

      MAJ_buffermap(&(db.tab_file[gp_file].buffer_map), pieces); 
      //MAJ du buffermap 
      
     
      tab_args[thread_number].cl = cl;
      tab_args[thread_number].bm = pieces;
      tab_args[thread_number].gp_file = gp_file;

      fprintf(stderr,"********\n");
      pthread_create(&tab[thread_number], NULL, download_pieces, &tab_args[thread_number]);
      fprintf(stderr,"++++++++\n");
      thread_number++;
   }
   pthread_join_all(tab, MAX_THREAD); 

   db.tab_file[gp_file].type = SEED;
}



